<?
// SQLQuery.php
// written by: David Fudge [ rkstar@mac.com ]
// created on: November 1, 2008
// last modified: November 1, 2008
//
// description:
// this file contains functions used for building an SQL query for our database API

// load dependencies here:
ed::load("Core.Core");
ed::load("Core.Error");
ed::load("Core.Utils");
ed::load("Data.XML");

class SQLQuery extends Core
{
	private $table;					// database table name
	private $limit;					// result limit
	private $selectFields;			// fields to return from database
	private $orderBy;				// order by
	private $groupBy;				// group by
	private $direction;				// ASC or DESC
	private $authMethod;			// the auth method XML object
	private $authGroups;			// array of open auth group XML elements
	private $fieldValues;			// field values XML object for insert/update queries
	private $sqlQuery;				// a string value of the parsed sql query

	public function __construct() { parent::__construct(); $this->init(); }
	public function init()
	{
		$this->startFieldValuesXml();
		$this->setSelectFields("*");
	}
	
	// set a field and value for that field
	public function set( $field, $value ) { $this->$field = $value; }
	public function reset()
	{
		$this->clearDBTable();
		$this->clearSelectFields();
		$this->clearOrder();
		$this->clearGroup();
		$this->clearDirection();
		$this->clearLimit();
		$this->clearValues();
		$this->clearAuthMethod();
	}
	
	// set the database table we want to query
	public function setDBTable( $table ) { $this->table = $table; }
	public function getDBTable() { return $this->table; }
	public function clearDBTable() { unset($this->table); }
	
	// set the select fields
	public function setSelectFields( $fields ) { $this->selectFields = $fields; }
	public function getSelectFields() { return $this->selectFields; }
	public function clearSelectFields() { $this->setSelectFields("*"); }
	
	// set the ordering conditions
	public function setOrder( $field ) { $this->orderBy = $field; }
	public function getOrder() { return $this->orderBy; }
	public function clearOrder() { unset($this->orderBy); }
	
	// set the grouping condition
	public function setGroup( $field ) { $this->groupBy = $field; }
	public function getGroup() { return $this->groupBy; }
	public function clearGroup() { unset($this->groupBy); }
	
	// set ascend/descend
	public function ascend() { $this->setAscend(); }
	public function descend() { $this->setDescend(); }
	public function setAscend() { $this->setAscending(); }
	public function setAscending() { $this->setDirection("ASC"); }
	public function setDescend() { $this->setDescending(); }
	public function setDescending() { $this->setDirection("DESC"); }
	public function setDirection( $direction ) { $this->direction = $direction; }
	public function getDirection() { return $this->direction; }
	public function clearDirection() { unset($this->direction); }
	
	// set the limit
	public function setLimit( $limit ) { $this->limit = $limit; }
	public function getLimit() { return $this->limit; }
	public function clearLimit() { unset($this->limit); }
	
	// get the query string
	public function setQuery( $string ) { $this->sqlQuery = $string; }
	public function getQuery() { return $this->sqlQuery; }
	
	private function startFieldValuesXml() { $this->fieldValues = new XML("<fields/>"); }
	// set a value for update/insert queries
	// this function can accept either a field name and a value for that field
	// or it can accept an associative array of field/value variables
	// or it can accept an object with $object->field = value setup
	public function setValue( $field, $value="" )
	{
		// sanity
		if( !is_object($this->fieldValues) ) { $this->startFieldValuesXml(); }
		
		// check for an object being passed in and
		// add a field value child to our XML
		if( !is_object($field) && !is_array($field) ) { $this->fieldValues->addChild($field,$value); return; }
		
		// check for an object vs assoc array
		$vars = (is_object($field)) ? get_object_vars($field) : $field;
		// loop thru the vars and set each one
		while( list($k,$v) = each($vars) ) { $this->setValue($k,$v); }
	}
	
	// send the fieldValues XML
	public function getValues() { return $this->fieldValues; }
	public function clearValues() { unset($this->fieldValues); }
	
	// open a new auth group
	public function openAuthGroup( $linkage="and" )
	{
		// sanity
		if( !is_object($this->authMethod) ) { $this->authMethod = new XML("<query/>"); }
		// group sanity
		if( !is_array($this->authGroups) ) { $this->authGroups = array(); }
		// check if there is an open auth group
		$addto = (count($this->authGroups) > 0) ? $this->authGroups[count($this->authGroups) - 1] : $this->authMethod;
		// add a group to the authMethod xml
		$group = $addto->addChild("authGroup");
		$group->addAttribute("linkage",$linkage);
		// push the xml reference on to the authGroups array
		array_push($this->authGroups,$group);
	}
	// close auth group based on LIFO
	public function closeAuthGroup()
	{
		// sanity
		if( count($this->authGroups) < 1 ) { return; }
		// pop the last element off the array
		array_pop($this->authGroups);
	}
	public function clearAuthGroups() { unset($this->authGroups); }

	// set the auth method
	public function setAuthMethod( $field, $value, $operator="=", $linkage="and" )
	{
		// sanity
		if( !is_object($this->authMethod) ) { $this->authMethod = new XML("<query/>"); }
		// value sanity
		$value = (strtolower($operator)=="like") ? "%".str_replace(" ","%",$value)."%" : $value;

		// check for open groups to add this child to
		$addto = (count($this->authGroups) > 0) ? $this->authGroups[count($this->authGroups) - 1] : $this->authMethod;
		// add the field child
		$child = $addto->addChild($field,$value);
		$child->addAttribute("operator",$operator);
		$child->addAttribute("linkage",$linkage);
	}
	public function clearAuthMethod() { unset($this->authMethod); }
	
	// generate a safe query string condition from our auth methods for use
	// in "select, update, and delete" queries
	public function getCondition( $node = false )
	{
		// sanity
		if( !is_object($node) ) { $node = $this->authMethod; }
		// make sure to reset our auth groups array at this point
		$this->authGroups = array();
		
		$authString = "";
		// sanity
		if( !$node || !$node->children() ) { return $authString; }
		// loop thru the child nodes of our authMethod XML object
		$i=0;
		foreach( $node->children() as $child )
		{
			// special case for "authGroup" nodes
			if( $child->getName() == "authGroup" )
			{
				$authString .= $child->attributes()->linkage." ( ";
				$authString .= $this->getCondition($child);
				$authString .= ")";
			}
			else
			{
				// linkage...
				$authString .= ($i > 0) ? $child->attributes()->linkage." " : "";
				// add the name of the field
				$authString .= $child->getName()." ".$child->attributes()->operator." ";
				// check for literal field values
				$authString .= ((substr($child,0,1)=="/") && (substr($child,-1)=="/")) ? substr($child,1,-1) : "\"".$child."\"";
			}
			$authString .= " ";
			
			// increment i
			$i++;
		}
		
		return substr($authString,0,-1);	// get rid of trailing space...
	}
	
	// DEBUGGING ONLY!!!
	public function showAuthMethod()
	{
		// DEBUGGING ONLY!!!
		if( !$node )
		{
			print "<pre>";
			print_r($this->authMethod);
			print htmlspecialchars(str_replace(">",">\n",$this->authMethod->asXML()));
			print "</pre>";
		}
	}
}
?>